package cc.shacocloud.mirage.restful.bind.support;

import cc.shacocloud.mirage.restful.HttpRequest;
import cc.shacocloud.mirage.restful.bind.WebDataBinder;
import cc.shacocloud.mirage.restful.bind.validation.Validated;
import cc.shacocloud.mirage.restful.exception.MethodArgumentNotValidException;
import cc.shacocloud.mirage.restful.util.BindingResultUtil;
import cc.shacocloud.mirage.utils.MethodParameter;
import io.vertx.core.Future;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;

/**
 * @author 思追(shaco)
 */
public abstract class AbstractArgumentValidateResolver {
    
    /**
     * 在解析值后调用。
     *
     * @param arg       已解析的参数值
     * @param name      参数名称
     * @param parameter 参数参数类型
     * @param request   当前请求
     */
    protected Future<Object> handleResolvedValue(@Nullable Object arg,
                                                 @Nullable String name,
                                                 @Nullable WebDataBinder binder,
                                                 @NotNull MethodParameter parameter,
                                                 @NotNull HttpRequest request) {
        if (Objects.nonNull(binder)) {
            // 参数校验
            validateIfApplicable(binder, parameter);
            
            if (isBindExceptionRequired(request, binder, parameter) && binder.hasErrors()) {
                return Future.failedFuture(new MethodArgumentNotValidException(parameter, binder.getBindingResult()));
            }
        }
        
        arg = adaptArgumentIfNecessary(arg, parameter);
        
        return Future.succeededFuture(arg);
    }
    
    
    /**
     * 如果需要，根据方法参数调整给定的参数。
     *
     * @param arg       解析后的参数
     * @param parameter 方法参数描述符
     * @return 已调整的参数，或原始已解决的参数
     * @see Optional
     */
    @Nullable
    protected Object adaptArgumentIfNecessary(@Nullable Object arg, @NotNull MethodParameter parameter) {
        // 支持 Optional
        if (parameter.getParameterType() == Optional.class) {
            if (arg == null || (arg instanceof Collection && ((Collection<?>) arg).isEmpty()) ||
                    (arg instanceof Object[] && ((Object[]) arg).length == 0)) {
                return Optional.empty();
            } else {
                return Optional.of(arg);
            }
        }
        return arg;
    }
    
    /**
     * 如果适用，验证绑定目标。
     * <p>
     * 默认实现检查{@code @jakarta.validation.Valid}和 {@link Validated}
     *
     * @param binder    要使用的数据生成器
     * @param parameter 方法参数描述符
     * @see #isBindExceptionRequired
     */
    protected void validateIfApplicable(@NotNull WebDataBinder binder, @NotNull MethodParameter parameter) {
        Validated validatedAnn = parameter.getParameterAnnotation(Validated.class);
        
        Annotation[] annotations = parameter.getParameterAnnotations();
        for (Annotation ann : annotations) {
            if (validatedAnn != null || ann.annotationType().getCanonicalName().equals("jakarta.validation.Valid")) {
                Class<?>[] hints = (validatedAnn != null ? validatedAnn.value() : new Class<?>[0]);
                binder.validate(hints);
                break;
            }
        }
    }
    
    /**
     * 是否在验证错误时引发致命绑定异常
     *
     * @param request   当前请求对象
     * @param binder    用于执行数据绑定的数据绑定器
     * @param parameter 方法参数描述符
     * @return 如果下一个方法参数的类型不是{@link Errors}，则为{@code true}
     * @see BindingResultUtil#isBindExceptionRequired
     */
    protected boolean isBindExceptionRequired(HttpRequest request, WebDataBinder binder, MethodParameter parameter) {
        return BindingResultUtil.isBindExceptionRequired(request, binder, parameter);
    }
    
}
